<template>
  <div ref="threeContainerRef"></div>
</template>

<script setup lang="ts">
import * as THREE from 'three'
// 引入dat.gui.js的一个类GUI
import { GUI } from 'three/examples/jsm/libs/lil-gui.module.min.js'
// 引入轨道控制器扩展库OrbitControls.js
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import { Mesh } from 'three'

// 实例化一个gui对象
const gui = new GUI()
//改变交互界面style属性
gui.domElement.style.right = '0px'
gui.domElement.style.width = '300px'
//创建一个对象，对象属性的值可以被GUI库创建的交互界面改变
const obj = {
  x: 30
}
// gui增加交互界面，用来改变obj对应属性
gui.add(obj, 'x', 0, 100).name('改变世界')

const threeContainerRef = useTemplateRef<HTMLDivElement>('threeContainerRef')
// 定义相机输出画布的尺寸(单位:像素px)
const width = ref<number>(window.innerWidth) //宽度
const height = ref<number>(window.innerHeight) //高度

// Three.js 对象引用
const scene = new THREE.Scene() // 创建3D场景对象Scene
const renderer = new THREE.WebGLRenderer({ antialias: true }) // 创建渲染器对象, 渲染器抗锯齿属性.antialias
// 获取你屏幕对应的设备像素比.devicePixelRatio告诉threejs,以免渲染模糊问题
renderer.setPixelRatio(window.devicePixelRatio)
renderer.setClearColor(0x008099, 1) //设置画布背景颜色
const clock = new THREE.Clock()
let camera: THREE.PerspectiveCamera // 实例化相机对象 30:视场角度, width / height:Canvas画布宽高比, 1:近裁截面, 3000：远裁截面
let controls: OrbitControls // 设置相机控件轨道控制器OrbitControls
let animationFrameId: number
let mesh: Mesh
let sptInterval: NodeJS.Timeout

let spt = clock.getDelta() * 1000 //毫秒

// 初始化场景
const initScene = () => {
  //创建一个长方体几何对象Geometry
  const geometry = new THREE.BoxGeometry(100, 100, 100)
  //地面
  const ground = new THREE.PlaneGeometry(10000, 10000)
  //创建一个长方体材质对象material
  const material = new THREE.MeshStandardMaterial({
    roughness: 0.5, //表面粗糙度
    color: 0xff0000 //0xff0000设置材质颜色为红色
    // transparent: true, // 开启透明
    // opacity: 0.5 //设置透明度
  })

  const groundMaterial = new THREE.MeshBasicMaterial({
    side: THREE.FrontSide, //默认只有正面可见
    color: 0xf4f4dd //0xff0000设置材质颜色为红色
  })
  mesh = new THREE.Mesh(ground, groundMaterial) //网格模型对象Mesh
  mesh.position.set(0, 0, 0)
  scene.add(mesh) //网格模型添加到场景中

  for (let i = -2; i < 3; i++) {
    for (let j = -2; j < 3; j++) {
      // 两个参数分别为几何体geometry、材质material
      mesh = new THREE.Mesh(geometry, material) //网格模型对象Mesh
      //设置网格模型在三维空间中的位置坐标，默认是坐标原点(0, 0, 0)
      mesh.position.set(i * 120, j * 150, 0)
      scene.add(mesh) //网格模型添加到场景中
    }
  }

  // 添加环境光
  const ambientLight = new THREE.AmbientLight(0xffffff, 0.5) // 颜色, 强度
  // 环境光强度
  gui.add(ambientLight, 'intensity', 0, 2)
  // 添加点光源
  const pointLight = new THREE.PointLight(0xffffff, 10000.0, 0, 1.1)
  // 平行光
  const directionalLight = new THREE.DirectionalLight(0xffffff, 1)

  // DirectionalLightHelper：可视化平行光
  const dirLightHelper = new THREE.DirectionalLightHelper(directionalLight, 5, 0xff0ff0)
  //点光源位置
  pointLight.position.set(300, 0, 0) //点光源放在中心上
  // 设置光源的方向：通过光源position属性和目标指向对象的position属性计算
  directionalLight.position.set(0, 0, 0)

  // 方向光指向对象网格模型mesh，可以不设置，默认的位置是0,0,0
  directionalLight.target = mesh

  scene.add(mesh)
  scene.add(ambientLight)
  scene.add(pointLight)
  scene.add(dirLightHelper)

  // 添加辅助工具（可选）
  const axesHelper = new THREE.AxesHelper(150)
  scene.add(axesHelper)
}

// 初始化相机
const initCamera = () => {
  if (!threeContainerRef.value) return

  width.value = window.innerWidth
  height.value = window.innerHeight

  camera = new THREE.PerspectiveCamera(30, width.value / height.value, 1, 10000)
  // 根据需要设置相机位置具体值
  camera.position.set(1000, 0, 0)
  //相机观察目标指向Threejs 3D空间中某个位置
  camera.lookAt(0, 0, 0) //坐标原点
}

// 初始化渲染器
const initRenderer = () => {
  if (!threeContainerRef.value) return

  renderer.setSize(width.value, height.value)
  renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)) // 限制像素比以提高性能
}

// 初始化控制器
const initControls = () => {
  if (!camera) return

  controls = new OrbitControls(camera, renderer.domElement)
  controls.enableDamping = true // 启用阻尼效果，使控制器更平滑
  controls.dampingFactor = 0.05
}

// 渲染循环
const animate = () => {
  animationFrameId = requestAnimationFrame(animate)

  if (controls) {
    controls.update() // 只在需要时更新控制器
  }

  spt = clock.getDelta() * 1000 //毫秒
  mesh.rotateY(0.01) //每帧绕y轴旋转0.01弧度

  renderer.render(scene, camera) //执行渲染操作
}

// 处理窗口大小变化
const handleResize = () => {
  if (!threeContainerRef.value || !camera) return

  const container = threeContainerRef.value
  width.value = container.clientWidth
  height.value = container.clientHeight

  camera.aspect = width.value / height.value // 更新画布宽高比
  camera.updateProjectionMatrix()
  renderer.setSize(width.value, height.value)
}

// 性能优化：防抖处理
let resizeTimeout: any

const debouncedResize = () => {
  clearTimeout(resizeTimeout)
  resizeTimeout = setTimeout(handleResize, 100)
}

// 监听容器大小变化（使用 ResizeObserver）
let resizeObserver: ResizeObserver
const initResizeObserver = () => {
  if (!threeContainerRef.value) return

  resizeObserver = new ResizeObserver((entries) => {
    for (const entry of entries) {
      if (entry.contentBoxSize) {
        debouncedResize()
      }
    }
  })

  resizeObserver.observe(threeContainerRef.value)
}

// 组件挂载
onMounted(() => {
  if (!threeContainerRef.value) return

  initScene()
  initCamera()
  initRenderer()
  initControls()
  initResizeObserver()

  threeContainerRef.value.appendChild(renderer.domElement)
  animate()

  sptInterval = setInterval(() => {
    console.log('帧率FPS', 1000 / spt)
  }, 1000)

  // 添加事件监听
  window.addEventListener('resize', debouncedResize)
})

// 组件卸载前的清理
onBeforeUnmount(() => {
  // 移除事件监听
  window.removeEventListener('resize', debouncedResize)
  if (resizeObserver) {
    resizeObserver.disconnect()
  }
  if (animationFrameId) {
    cancelAnimationFrame(animationFrameId)
  }
  if (sptInterval) {
    clearInterval(sptInterval)
  }

  // 清理 Three.js 资源
  scene.traverse((object) => {
    if (object instanceof THREE.Mesh) {
      object.geometry.dispose()
      if (Array.isArray(object.material)) {
        object.material.forEach((material) => material.dispose())
      } else {
        object.material.dispose()
      }
    }
  })

  renderer.dispose()
  controls?.dispose()
})
</script>

<style scoped lang="scss"></style>
